package implementacionJ48;


import java.util.Enumeration;

import weka.core.Instance;
import weka.core.Instances;
import weka.core.Attribute;

public class Gain {

	public static double getGanancia(Instances S, Attribute attr) throws Exception{
		double infoX = Entropy.calculateInfoX(S, attr);
		double info = Entropy.calculateInfo(S);
		double F = Entropy.calculateF(S, attr);
		
		return (F)*(info - infoX);

	}
	
	public static double getProporcionGanancia(Instances S, Attribute attr) throws Exception{
		double gainRatio = 0.0;
		double mejorGainRatio = 0.0;
		int indice = attr.index();
		
	
		double[] Ti = TestData.selectTiSubsetValue(S, S.attribute(indice));
		double splitInfoVal = SplitInfo.calculateSplitInfo(S, Ti, indice);
		if(splitInfoVal != 0){
			double ganancia = getGanancia(S, attr);
			gainRatio = (ganancia / splitInfoVal) ;
		}
			
		
		System.out.print("gainRatio " + attr.name() + ": "  + gainRatio);
		System.out.print("\n");
		
		return gainRatio;
	}
	
	/* resolverEmpates(S, indicesEmpates)*/
	public static int resolverEmpates(Instances S, int indicesEmpates[]) throws Exception{
		
		/* Recorremos todos los atributos tratando de calcular el error asociado(la cant de clases mal ubicadas) */
		double ErrorAttrAsociado[] = new double[S.numAttributes()-1];
		for(int j = 0; j < ErrorAttrAsociado.length; j++)
			ErrorAttrAsociado[j] = -1;
		
		for(int i = 0; i < S.numAttributes() - 1; i++){
			Attribute attr = S.attribute(i);
			
			/* Vemos en cuales atributos hubo empate*/
			if(indicesEmpates[i] != -1){
				/* Seteamos los numeros de valores(si son datos numericos, hay solo dos valores) */
				int numValues = 0;
				if(attr.isNumeric())
					numValues = 2;
				else
					numValues = attr.numValues();
				
				/* Calculamos la suma de errores asociados para todos las selecciones(hijos de S y cada Attr */
				double attrValue = 0.0;
				for(int j = 0; j < numValues; j++){
					Instances Sattr = TestData.selectInstancesWhere(S, attr, attrValue);
					ErrorAttrAsociado[i] += TestData.calculateEcasosCubiertosErr(Sattr);
					attrValue +=1.0;
				}
			}
		}
		
		/* Buscamos en el vector el que tenga menor error asociado(este seria el mejor atributo para
		 * elegirlo como bestAttribute */
		double menorError = 0.0;
		int indiceMenorError = -1;
		for(int i = 0; i < S.numAttributes() -1; i++){
			if(menorError >= ErrorAttrAsociado[i] && ErrorAttrAsociado[i] != -1)
				indiceMenorError = i;
		}
		
		
		return indiceMenorError;
		
	}
	/* Aqui se aplican tambien los criterios de parada */
	/* Retorna el atributo que tiene el mejor valor de Ganancia()*/
	public static Attribute getBestAttribute(Instances S, double porcentajeCerteza) throws Exception{
		double valoresGanancia[]  = new double[S.numAttributes()-1];
		int indiceMayorGanancia = -1;
		
		
		/* Si las instancias ya estan completamente clasificadas retornamos null */
		if( Entropy.calculateInfo(S) <= 0.0 )
			return null;
		
		/* Si ya sobrepasamos el porcentaje de certeza requerido por el usuario, clasificamos a un nodo hoja */
		double porcentajeActual = 1.0 - TestData.calculateEcasosCubiertosErr(S)/TestData.calculateNcasosCubiertos(S);
		if( porcentajeActual >= porcentajeCerteza)
			return null;
		
		/* Traemos la proporcion de ganancia de cada uno de los atributos por las cuales podemos dividir */
		for(int i = 0; i < S.numAttributes() -1; i++){
			Attribute testAttr = S.attribute(i);
			valoresGanancia[i] = getProporcionGanancia(S, testAttr);
		}
		
		/* Buscamos el que tiene mayor ganancia */
		double mayorGanancia = 0.0;
		for(int j = 0; j < valoresGanancia.length; j++){
			if(mayorGanancia <= valoresGanancia[j]){
				mayorGanancia = valoresGanancia[j];
				indiceMayorGanancia = j;
			}
		}
		
		/* Chequeamos si hubo empates entre las mayores ganancias */
		int cantEmpates = 0;
		int indicesEmpates[] = new int[valoresGanancia.length];
		for(int i = 0; i < valoresGanancia.length; i++){
			/* Chequeamos cuantos atributos retornaron el mismo valor de 'mayorGanancia' */
			if(valoresGanancia[i] == mayorGanancia){
				cantEmpates++;
				indicesEmpates[i] = i;
			}else{
				indicesEmpates[i] = -1;
			}
				
		}
		/* Si hubo empates, lo resolvemos elegantemente */
		if(cantEmpates > 1){
			int indiceEmpate = resolverEmpates(S, indicesEmpates);
			if(indiceEmpate != -1)
				indiceMayorGanancia = indiceEmpate;
		}
		
		if(mayorGanancia == 0.0 )
			return null;
		
		//if(indiceMayorGanancia == -1)
			//return null;
			
		return S.attribute(indiceMayorGanancia);
		
	}
	
}
